import logging
from typing import Dict, Any

import networkx as nx
from pyvis.network import Network

from 程序核心代码.ast节点.节点分析.可视化.可视化器基类 import 可视化器基类
from 程序核心代码.ast节点.节点定义.基础定义.节点异常定义 import 节点异常

logger = logging.getLogger(__name__)

class 依赖图可视化器(可视化器基类):
    """依赖图可视化器类，用于可视化节点间的依赖关系"""
    
    def __init__(self):
        super().__init__()
        self.图 = nx.DiGraph()
        self.网络 = Network(
            height="750px",
            width="100%",
            bgcolor="#ffffff",
            font_color="#333333"
        )
        self.节点样式 = {
            "变量节点": {"color": "#4CAF50", "shape": "dot"},
            "常量节点": {"color": "#2196F3", "shape": "square"},
            "属性节点": {"color": "#9C27B0", "shape": "triangle"},
            "条件ast节点基础模型": {"color": "#FF9800", "shape": "diamond"},
            "循环ast节点基础模型": {"color": "#F44336", "shape": "box"},
            "运算节点": {"color": "#795548", "shape": "ellipse"}
        }
        
    def 可视化(self, 数据: Dict[str, Any]) -> str:
        """将依赖关系数据可视化为交互式图形"""
        try:
            self.数据 = 数据
            self._构建依赖图()
            self._应用样式()
            self._添加交互()
            
            return self._生成HTML()
            
        except Exception as e:
            logger.error(f"依赖图可视化失败: {str(e)}")
            raise 节点异常(f"依赖图可视化失败: {str(e)}")
            
    def _构建依赖图(self) -> None:
        """构建依赖关系图"""
        # 添加节点
        for 节点ID, 节点信息 in self.数据["节点信息"].items():
            self.图.add_node(
                节点ID,
                label=节点信息.get("名称", 节点ID),
                title=self._生成节点提示(节点信息),
                类型=节点信息.get("类型", "未知")
            )
            
        # 添加边
        for 源节点, 目标节点集 in self.数据["依赖关系"].items():
            for 目标节点 in 目标节点集:
                self.图.add_edge(
                    源节点,
                    目标节点,
                    title="依赖",
                    arrows="to"
                )
                
    def _应用样式(self) -> None:
        """应用节点和边的样式"""
        self.网络.from_nx(self.图)
        
        for 节点 in self.网络.nodes:
            节点类型 = 节点.get("类型", "未知")
            样式 = self.节点样式.get(节点类型, {"color": "#999999", "shape": "dot"})
            节点.update(样式)
            
    def _添加交互(self) -> None:
        """添加交互功能"""
        self.网络.toggle_physics(True)
        self.网络.show_buttons(filter_=['physics'])
        
        # 添加事件处理
        self.网络.set_options("""
        var options = {
            "physics": {
                "forceAtlas2Based": {
                    "gravitationalConstant": -50,
                    "centralGravity": 0.01,
                    "springLength": 100,
                    "springConstant": 0.08
                },
                "maxVelocity": 50,
                "minVelocity": 0.1,
                "solver": "forceAtlas2Based"
            },
            "interaction": {
                "hover": true,
                "tooltipDelay": 300,
                "zoomView": true,
                "dragView": true
            }
        }
        """)
        
    def _生成节点提示(self, 节点信息: Dict[str, Any]) -> str:
        """生成节点的悬停提示信息"""
        提示 = [
            f"ID: {节点信息.get('id', '未知')}",
            f"类型: {节点信息.get('类型', '未知')}",
            f"名称: {节点信息.get('名称', '未知')}"
        ]
        
        if "图形项属性" in 节点信息:
            提示.append("图形项属性:")
            for 键, 值 in 节点信息["图形项属性"].items():
                提示.append(f"  {键}: {值}")
                
        return "\n".join(提示)
        
    def _生成HTML(self) -> str:
        """生成可视化HTML"""
        try:
            模板 = self.加载模板("依赖图")
            图形数据 = self.网络.generate_html()
            
            return 模板.replace(
                "<!-- GRAPH_PLACEHOLDER -->",
                图形数据
            )
            
        except Exception as e:
            logger.error(f"HTML生成失败: {str(e)}")
            raise 节点异常(f"HTML生成失败: {str(e)}")
            
    def 导出图片(self, 文件名: str) -> str:
        """将依赖图导出为图片"""
        try:
            import matplotlib.pyplot as plt
            
            plt.figure(figsize=(12, 8))
            pos = nx.spring_layout(self.图)
            
            # 绘制节点
            for 类型, 样式 in self.节点样式.items():
                节点列表 = [n for n, attr in self.图.nodes(data=True) 
                        if attr.get("类型") == 类型]
                nx.draw_networkx_nodes(
                    self.图, pos,
                    nodelist=节点列表,
                    node_color=样式["color"],
                    node_shape=样式["shape"],
                    node_size=500
                )
                
            # 绘制边
            nx.draw_networkx_edges(
                self.图, pos,
                edge_color="#666666",
                arrows=True,
                arrowsize=20
            )
            
            # 添加标签
            标签 = nx.get_node_attributes(self.图, "label")
            nx.draw_networkx_labels(self.图, pos, 标签)
            
            # 保存图片
            plt.axis("off")
            图片路径 = os.path.join(self.配置["输出目录"], f"{文件名}.png")
            plt.savefig(图片路径, bbox_inches="tight", dpi=300)
            plt.close()
            
            return 图片路径
            
        except Exception as e:
            logger.error(f"图片导出失败: {str(e)}")
            raise 节点异常(f"图片导出失败: {str(e)}")
            
    def 重置(self) -> None:
        """重置可视化器状态"""
        super().清理数据()
        self.图.clear()
        self.网络 = Network(
            height="750px",
            width="100%",
            bgcolor="#ffffff",
            font_color="#333333"
        ) 